import { EventEmitter } from "events";
import SocketService from "@/utils/socketService";

interface IMessage<T = any> {
  push_type: string;
  object_name?: string;
  form_uid?: string;
  to_uid?: string;
  to_group_id?: string;
  content?: string;
  data?: T;
}

enum ConnectionState {
  Disconnected = "Disconnected",
  Connecting = "Connecting",
  Connected = "Connected",
  Error = "Error",
}

class WebSocketService extends EventEmitter {
  private socket: WebSocket | null = null;
  private connectionState: ConnectionState = ConnectionState.Disconnected;
  private readonly url: string;
  private reconnectAttempts: number = 0;
  private reconnectTimeoutId: NodeJS.Timeout | null = null;
  private heartbeatIntervalId: NodeJS.Timeout | null = null;
  private heartbeatInterval: number = 5000;
  private messageQueue: IMessage[] = [];

  constructor(url: string) {
    super();
    this.url = url;
  }

  public connect() {
    if (this.connectionState !== ConnectionState.Disconnected) return;

    this.socket = new WebSocket(this.url);
    this.socket.onopen = () => this.onOpen();
    this.socket.onclose = () => this.onClose();
    this.socket.onerror = (error) => this.onError(error);
    this.socket.onmessage = (event) => this.onMessage(event);
  }

  public disconnect() {
    this.clearHeartbeat();
    this.cancelReconnect();
    if (this.socket && this.socket.readyState === WebSocket.OPEN) {
      this.socket.close();
    }
    this.socket = null;
    this.messageQueue = [];
    this.connectionState = ConnectionState.Disconnected;
  }

  public send<T>(message: IMessage<T>) {
    if (this.socket && this.socket.readyState === WebSocket.OPEN) {
      this.socket.send(JSON.stringify(message));
    } else {
      this.messageQueue.push(message);
    }
  }

  private startHeartbeat() {
    this.heartbeatIntervalId = setInterval(() => {
      this.send({ push_type: "ping" });
    }, this.heartbeatInterval);
  }

  private clearHeartbeat() {
    if (this.heartbeatIntervalId) {
      clearInterval(this.heartbeatIntervalId);
      this.heartbeatIntervalId = null;
    }
  }

  private cancelReconnect() {
    if (this.reconnectTimeoutId) {
      clearTimeout(this.reconnectTimeoutId);
      this.reconnectTimeoutId = null;
    }
  }

  private onOpen() {
    console.log("WebSocket connected");
    this.connectionState = ConnectionState.Connected;
    this.startHeartbeat();
    this.emit("connected");
    this.flushMessageQueue();
  }

  private onClose() {
    console.log("WebSocket closed");
    this.clearHeartbeat();
    this.connectionState = ConnectionState.Disconnected;
    this.reconnect();
    this.emit("disconnected");
  }

  private onError(error: Event) {
    console.error("WebSocket error:", error);
    this.connectionState = ConnectionState.Error;
    this.reconnect();
    this.emit("error", error);
  }

  private onMessage(event: MessageEvent) {
    const message = JSON.parse(event.data) as IMessage;
    console.log("Received message:", message);
    this.emit(`message:${message.push_type}`, message);
    this.emit("message", message);
  }

  private reconnect() {
    this.cancelReconnect();
    const delay = Math.min(60000, 1000 * Math.pow(2, this.reconnectAttempts));
    this.reconnectTimeoutId = setTimeout(() => {
      this.reconnectAttempts++;
      this.connect();
    }, delay);
  }

  private flushMessageQueue() {
    for (const message of this.messageQueue) {
      this.send(message);
    }
    this.messageQueue = [];
  }
}

